using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Data;
using System.Drawing;
using System.Text;
using System.Windows.Forms;
using MacomberMap.Common.Internals;
using MacomberMap.Common.Database_Interface;
using MacomberMap.Common.Database_Interface.CIM;
using MacomberMap.Loader.User_Interfaces.One_Lines;
using MacomberMap.Loader.User_Interfaces.Network_Map;
using MacomberMap.Common.Logging;
using MacomberMap.Common.Events;
using MacomberMap.Loader.Export;
using MacomberMap.Common.User_Interfaces.Events;
using MacomberMap.Common.Database_Interface.CIM.CIM_Interface;
using MacomberMap.Loader.Merging;
using System.Reflection;
using MacomberMap.Common.Database_Interface.CIM.User_Interfaces;
using MacomberMap.Common.Database_Interface.CIM.One_Line_Interface;
using System.IO;
using MacomberMap.Loader.Export.NOMCRs;
using System.Xml;
using Oracle.DataAccess.Client;


namespace MacomberMap.Loader.User_Interfaces.CIM
{
    /// <summary>
    /// This class handles the user interface for CIM
    /// </summary>
    public partial class frmCIM : Form
    {

        #region Variable declarations
        /// <summary>Our collection of old models</summary>
        private Dictionary<String, int> OldModels = new Dictionary<string, int>();

        /// <summary>Whether synchronization should be performed</summary>
        public static bool PerformSynch = false;

        /// <summary>The database connection to our repository</summary>
        public MM_Database_Oracle RepositoryDb;

        /// <summary>The database connection to CIM</summary>
        public MM_Database_CIM CIMDb;

        /// <summary>Whether the user has started the runs.</summary>
        private bool RunsStarted = false;        

        /// <summary>The class to synchronize data</summary>
        private MM_CIM_Oracle_Synchronization Synch;

        /// <summary>The repository for the coordinate file</summary
        private string CoordinateFile = null;

        /// <summary>Our Oracle connector</summary>
        private OracleConnection oNewNMMS;

        #endregion

        #region Initialization
        /// <summary>
        /// Initialize the CIM viewer form
        /// </summary>
        public frmCIM(MM_Database_Oracle DbConnection, MM_Database_CIM CIMDb)
        {            
            InitializeComponent();
            
            //JLH
            //this.CIMDb = CIMDb;
            //XmlElement xConfig = MM_Configuration.xConfig["MacomberMap_Loader"]["OneLines"];            
            //this.oNewNMMS = new OracleConnection(String.Format("Data Source={0};User ID={1};Password={2}", xConfig.Attributes["NMMSDatabase"].Value, xConfig.Attributes["NMMSUserName"].Value, xConfig.Attributes["NMMSPassword"].Value));
            //oNewNMMS.Open();
            
            //JLH
            //Load in our list of old models
            //using (OracleCommand oCmd = new OracleCommand("SELECT ID, NAME, VALIDSTART FROM MM_DATABASE_MODEL ORDER BY VALIDSTART, NAME", oNewNMMS))
            //using (OracleDataReader oRd = oCmd.ExecuteReader())
            //while (oRd.Read())
            //{
            //    String Name = (string)oRd["NAME"];
            //    DateTime ValidStart = Convert.ToDateTime(oRd["VALIDSTART"]);
            //    OldModels.Add(ValidStart.ToShortDateString() + " - " + Name, Convert.ToInt32(oRd["ID"]));
            //    cmbPriorModel.Items.Add(ValidStart.ToShortDateString() + " - " + Name);
            //}
            
            this.lblVersion.Text = "Version " + Application.ProductVersion + " (Core " + MM_Repository.BaseVersion + ")";
            this.lblFileName.Text = "";
            this.RepositoryDb = DbConnection;
            picLoadProgress.Paint += UpdateLoadProgess;
            //MM_Coordinate_Interface.LoadCoordinates(DbConnection);
            chkNodeToResourceNode.Checked = MacomberMap.Common.Database_Interface.CIM.Components.CIM_Element.ResourceNodesFromConnectivityNodes;
           
        }
        

        /// <summary>
        /// When the form is first shown, start our update process
        /// </summary>
        /// <param name="e"></param>
        protected override void OnShown(EventArgs e)
        {
                //evLog.AssignEventHandlers(TestIncludeEvent);

                //Invite the user to choose a CIM file.
                if (CIMDb == null)
                {
                    OpenFileDialog o = new OpenFileDialog();
                    o.Title = "Open a CIM database";
                    o.Filter = "CIM Databases and Indexes (*.xml;*.CimIndex;*.idx)|*.xml;*.CimIndex;*.idx|CIM Database (*.xml)|*.xml|CIM Index (*.CimIndex;*.idx)|*.CimIndex;*.idx|All files (*.*)|*.*";
                    if (o.ShowDialog() == DialogResult.OK)
                    {
                        CIMDb = new MM_Database_CIM(o.FileName);
                        lblFileName.Text = "File Name: " + o.SafeFileName;
                        //evLog.AssignConnectors(CIMDb, RepositoryDb);
                    }
                }

                MM_Repository.Databases.Add(CIMDb);
                MM_Repository.Databases.Add(RepositoryDb);
                tmrPicUpdate.Enabled = true;
        }

        /// <summary>
        /// Determine whether an event should be added, or auto-processed.
        /// </summary>
        /// <param name="Event"></param>
        /// <returns></returns>
        private bool TestIncludeEvent(MM_Event Event)
        {
            //If the "auto-approve" checkbox has been checked, approve changes and change states as appropriate.
            if (chkAutoApprove.Checked)
            {
                MethodInfo mI = Event.GetType().GetMethod("ApproveChange");
                if (mI != null)
                    try
                    {
                        mI.Invoke(Event, new object[] { new MM_Database[] { CIMDb, RepositoryDb } });
                        Event.EventState = MM_Event.EventStateEnum.Closed;
                    }
                    catch (Exception ex)
                    {
                        if (Event is MM_Event_NewElement)
                            Console.WriteLine("Error processing new element: " + (Event as MM_Event_NewElement).NewElement.ToString() + ": " + ex.Message + "\n" + ex.InnerException.Message + "\n" + ex.InnerException.StackTrace);
                        else
                            Console.WriteLine("Error processing event: " + ex.Message + "\n" + ex.InnerException.Message + "\n" + ex.InnerException.StackTrace);
                    }
                else
                    Event.EventState = MM_Event.EventStateEnum.Acknowledged;
            }
            return true;
        }

        #endregion

        #region Status updates
        /// <summary>
        /// Every second, update the status of our CIM load
        /// </summary>
        /// <param name="sender"></param>
        /// <param name="e"></param>
        private void UpdateCIMStatus(object sender, EventArgs e)
        {
            try
            {
                //long Mem;
                //tsCPUUse.Text = "CPU: " + MM_Process_Details.GetCPUUsage(out Mem) + "%";
                //if (Mem > 1024 * 1024 * 1024)
                //    tsMemUse.Text = "Mem: " + (Mem / (1024 * 1024)).ToString("#,##0 M");
                //else if (Mem > 1024 * 1024)
                //    tsMemUse.Text = "Mem: " + (Mem / (1024)).ToString("#,##0 K");
                //else
                //    tsMemUse.Text = "Mem: " + Mem.ToString("#,##0");


                if (RunsStarted)
                {
                    //JLH
                    foreach (ListViewItem lvI in lvTasks.Items)
                    {
                        MM_Exporter Exp = lvI.Tag as MM_Exporter;
                        if (Exp.ProcessThread.ThreadState == System.Threading.ThreadState.Unstarted)
                        {
                            lvI.SubItems[1].Text = "Not started.";
                            lvI.SubItems[2].Text = "";
                            lvI.SubItems[3].Text = "";

                        }
                        else if (Exp.ProcessThread.ThreadState == System.Threading.ThreadState.Running)
                        {
                            Double Progress = Exp.Progress;
                            lvI.SubItems[1].Text = Progress.ToString("0.0%");
                            TimeSpan RunTime = DateTime.Now - Exp.StartTime;
                            lvI.SubItems[2].Text = RunTime.ToString();
                            if (RunTime.TotalSeconds > 10 && Exp.Progress > 0)
                                lvI.SubItems[3].Text = TimeSpan.FromSeconds((RunTime.TotalSeconds / Exp.Progress) * (1.0 - Progress)).ToString();

                        }
                        else if (Exp.ProcessThread.ThreadState == System.Threading.ThreadState.Stopped)
                        {
                            if (Exp.threadFailed)
                            {
                                lvI.SubItems[1].Text = "Failed.";
                                lvI.SubItems[2].Text = "";
                                lvI.SubItems[3].Text = Exp.tFailedReason ;
                            }
                            else
                            {
                                lvI.SubItems[1].Text = "Completed.";
                                lvI.SubItems[2].Text = (Exp.EndTime - Exp.StartTime).ToString();
                                lvI.SubItems[3].Text = "";
                            }
                        }
                    }

                }
                else if (PerformSynch && MM_Database_CIM.CIMLoader.Completed && Synch == null)
                    Synch = new MM_CIM_Oracle_Synchronization(RepositoryDb, CIMDb, chkDbLoad.Checked, CoordinateFile);
                else if (MM_Database_CIM.CIMLoader.Completed && (PerformSynch ? Synch.Completed : true) && !RunsStarted)
                {
                    RunsStarted = true;
                    int PicHeight1 = picLoadProgress.Top;
                    int PicHeight = grpCIMTools.Top - lblFileName.Top;
                    this.Controls.Remove(picLoadProgress);
                    picLoadProgress.Dispose();
                    grpModelInfo.Top -= PicHeight;
                    grpCIMTools.Top -= PicHeight;
                    grpDatabase.Top -= PicHeight;
                    grpExecution.Top -= PicHeight;
                    btnExecuteSteps.Top -= PicHeight;
                    Height -= PicHeight;
                    this.lblFileName.Top = PicHeight1;

                    //If requested, add in our model to the database
                    if (chkAddModel.Checked)
                        using (OracleCommand oCmd = new OracleCommand("INSERT INTO MM_DATABASE_MODEL (ID, NAME, EXPORTEDON, VALIDSTART, FULLPATH, PRIORMODEL, ELEMENTCOUNT, TEIDCOUNT, MAXTEID, MAXMRID) VALUES (SEQ_MM_DATABASE_MODEL.NEXTVAL, :0, :1, :2, :3, :4, :5, :6, :7, :8)", oNewNMMS))
                        {
                            oCmd.Prepare();
                            oCmd.Parameters.Add("0", Path.GetFileNameWithoutExtension(MM_Database_CIM.CIMFileName));
                            oCmd.Parameters.Add("1", new FileInfo(MM_Database_CIM.CIMFileName).CreationTime);
                            oCmd.Parameters.Add("2", dtModelStartDate.Value);
                            if (MM_Database_CIM.CIMFileName.StartsWith("\\"))
                                oCmd.Parameters.Add("3", Path.GetDirectoryName(MM_Database_CIM.CIMFileName));
                            else
                                oCmd.Parameters.Add("3", @"\\" + Environment.MachineName + @"\" + MM_Database_CIM.CIMFileName.Substring(0, 1) + "$" + Path.GetDirectoryName(MM_Database_CIM.CIMFileName.Substring(2)));

                            //Find the prior model
                            using (OracleCommand oCmd2 = new OracleCommand("SELECT ID FROM MM_DATABASE_MODEL WHERE Name='" + cmbPriorModel.Text.Split('-')[1].Trim() + "'", oNewNMMS))
                                oCmd.Parameters.Add("4", oCmd2.ExecuteScalar());
                            oCmd.Parameters.Add("5", MM_Database_CIM.Elements.Count);
                            oCmd.Parameters.Add("6", MM_Database_CIM.TEIDs.Count);
                            UInt64 MaxTEID = 0;
                            foreach (UInt64 TEID in MM_Database_CIM.TEIDs.Keys)
                                MaxTEID = Math.Max(MaxTEID, TEID);
                            oCmd.Parameters.Add("7", MaxTEID);
                            oCmd.Parameters.Add("8", MM_Database_CIM.MaxMRID);
                            oCmd.ExecuteNonQuery();
                        }

                    MM_Log.LogEvent(new MM_Event_Information("Completed loading in CIM file: " + (DateTime.Now - MM_Database_CIM.CIMLoader.StartTime), this));

                    new MM_Exporter_SQLite(lvTasks);
                    //Add in our CRR exporter.
                    //new MM_Exporter_CRR("CRRExport.MM_CRR", RepositoryDb as MM_Database_Oracle, CIMDb, lvTasks);
                    new MM_Exporter_OS(MM_Configuration.xConfig["MacomberMap_Loader"]["OutageScheduler"].Attributes["TargetDirectory"].Value, CIMDb as MM_Database_CIM, lvTasks);
                    new MM_Exporter_Contingencies(MM_Configuration.xConfig["MacomberMap_Loader"]["ContingencyDefinitions"].Attributes["TargetDirectory"].Value, lvTasks, RepositoryDb as MM_Database_Oracle, CIMDb as MM_Database_CIM);
                    //new MM_Exporter_OneLine(dtModelStartDate.Value, OldModels[cmbPriorModel.Text], MM_Configuration.xConfig["MacomberMap_Loader"]["OneLines"], lvTasks, RepositoryDb as MM_Database_Oracle, CIMDb as MM_Database_CIM, oNewNMMS);
                    //new MM_Exporter_MMXML(Path.GetDirectoryName(MM_Database_CIM.CIMFileName), CIMDb, lvTasks);
                    this.Refresh();
                }
                else
                    picLoadProgress.Refresh();
            }
            catch (Exception ex)
            {
                //File.WriteAllText("Error-UpdateCIMStatus-" + DateTime.Now.ToString("yyyy-MM-dd-HH-mm-ss") + ".log", ex.ToString());
            }
        }

        /// <summary>
        /// Update our loading process
        /// </summary>
        /// <param name="sender"></param>
        /// <param name="e"></param>
        private void UpdateLoadProgess(object sender, PaintEventArgs e)
        {
            try
            {
                if (MM_Database_CIM.CIMLoader == null)
                    return;
                else
                {
                    e.Graphics.Clear(SystemColors.Control);

                    if (!MM_Database_CIM.CIMLoader.Completed || (Synch != null && !Synch.Completed))
                    {
                        //Determine our percentage and estmated time remaining
                        long Position = !MM_Database_CIM.CIMLoader.Completed ? MM_Database_CIM.CIMLoader.Position : Synch.CurElement;
                        long FileSize = !MM_Database_CIM.CIMLoader.Completed ? MM_Database_CIM.CIMLoader.FileSize : Synch.NumElements;
                        if (FileSize == 0)
                            return;

                        string Title = !MM_Database_CIM.CIMLoader.Completed ? "Loading" : "Synchronizing";
                        float Percentage = (float)Position / (float)FileSize;

                        int CurWidth = (int)(picLoadProgress.Width * Percentage);
                        e.Graphics.FillRectangle(Brushes.LightBlue, new Rectangle(0, 0, CurWidth, picLoadProgress.Height));
                        e.Graphics.DrawRectangle(Pens.Black, new Rectangle(0, 0, picLoadProgress.Width - 1, picLoadProgress.Height - 1));
                        using (StringFormat sF = new StringFormat())
                        {
                            sF.Alignment = StringAlignment.Center;
                            sF.LineAlignment = StringAlignment.Center;
                            if (Position > 0)
                            {
                                TimeSpan RunTime = DateTime.Now - MM_Database_CIM.CIMLoader.StartTime;
                                long Remaining = FileSize - Position;
                                Double Rem = Remaining * (RunTime.TotalSeconds / Position);
                                String RemStr = "Infinite";
                                if (Rem < TimeSpan.MaxValue.TotalSeconds)
                                {
                                    TimeSpan RemainingTime = TimeSpan.FromSeconds(Rem);
                                    RemStr = RemainingTime.TotalHours.ToString("00") + ":" + RemainingTime.Minutes.ToString("00") + ":" + RemainingTime.Seconds.ToString("00");
                                }
                                e.Graphics.DrawString(Title + " " + Percentage.ToString("0.0%") + " | " + RemStr, SystemFonts.CaptionFont, Brushes.Black, picLoadProgress.DisplayRectangle, sF);
                            }
                            else
                                e.Graphics.DrawString(Title + " " + Percentage.ToString("0.0%"), SystemFonts.CaptionFont, Brushes.Black, picLoadProgress.Bounds, sF);
                        }
                    }
                }
            }
            catch (Exception ex)
            {
                File.WriteAllText("MMLoader-PaintError-" + DateTime.Now.ToString("yyyy-MM-dd-HH-mm-ss") + ".log", ex.ToString());
            }
        }
        #endregion

        #region User Interactions
        /// <summary>
        /// Start up the CIM browser
        /// </summary>
        /// <param name="sender"></param>
        /// <param name="e"></param>
        private void btnCIMBrowser_Click(object sender, EventArgs e)
        {
          

            new frmCIMBrowser().Show();
        }


        /// <summary>
        /// View the substation collection
        /// </summary>
        /// <param name="sender"></param>
        /// <param name="e"></param>
        private void btnEditSubstations_Click(object sender, EventArgs e)
        {
                     
            if (MM_Repository.Substations.Count == 0)
                try
                {
                    RepositoryDb.LoadCompanies();
                    RepositoryDb.LoadSubstations();
                    RepositoryDb.LoadLines();
                }
                catch (Exception ex)
                {
                    MessageBox.Show("Error loading in substations and lines: " + ex.Message + "\n" + ex.StackTrace, Application.ProductName, MessageBoxButtons.OK, MessageBoxIcon.Warning);
                }
            new frmNetworkMapEditor().Show();

        }

        /// <summary>
        /// Import a new one-line
        /// </summary>
        /// <param name="sender"></param>
        /// <param name="e"></param>
        private void btnImportOneLines_Click(object sender, EventArgs e)
        {
            frmOneLineEditor LineEditor = new frmOneLineEditor(RepositoryDb, CIMDb);
                LineEditor.Show();           
        }
        #endregion

        /// <summary>
        /// Execute the selected steps
        /// </summary>
        /// <param name="sender"></param>
        /// <param name="e"></param>
        private void btnExecuteSteps_Click(object sender, EventArgs e)
        {
            
            foreach (ListViewItem lvI in lvTasks.Items)
                if (lvI.Checked)
                    (lvI.Tag as MM_Exporter).Start();
        }

        /// <summary>
        /// Start a transaction
        /// </summary>
        /// <param name="sender"></param>
        /// <param name="e"></param>
        private void btnStartTransaction_Click(object sender, EventArgs e)
        {
            RepositoryDb.StartTransaction();
        }

        /// <summary>
        /// Commit the transaction
        /// </summary>
        /// <param name="sender"></param>
        /// <param name="e"></param>
        private void btnCommitTransaction_Click(object sender, EventArgs e)
        {
            RepositoryDb.CommitTransaction();
        }

        /// <summary>
        /// Roll back the transaction
        /// </summary>
        /// <param name="sender"></param>
        /// <param name="e"></param>
        private void btnCancelTransaction_Click(object sender, EventArgs e)
        {
            RepositoryDb.CancelTransaction();
        }

        /// <summary>
        /// Update the synchronization procedure with the database load status
        /// </summary>
        /// <param name="sender"></param>
        /// <param name="e"></param>
        private void chkDbLoad_CheckedChanged(object sender, EventArgs e)
        {
            if (Synch != null)
                Synch.InitialLoad = chkDbLoad.Checked;


            using (OpenFileDialog oFd = new OpenFileDialog())
            {
                oFd.Title = "Select substation long name/coordinate file";
                oFd.Filter = "Comma-delimited files (*.csv)|*.csv";
                if (oFd.ShowDialog() == DialogResult.OK)
                    CoordinateFile = oFd.FileName;
                else
                    CoordinateFile = null;
            }
        }

        /// <summary>
        /// If the user double-clicks on the progress bar and is in the midst of a merge process, cancel it
        /// </summary>
        /// <param name="sender"></param>
        /// <param name="e"></param>
        private void picLoadProgress_DoubleClick(object sender, EventArgs e)
        {
            if (Synch != null && Synch.ProcessThread.ThreadState == System.Threading.ThreadState.Running && MessageBox.Show("Are you sure you want to abort MM Database <-> CIM synchronization?", Application.ProductName, MessageBoxButtons.YesNo, MessageBoxIcon.Question) == DialogResult.Yes)
            {
                Synch.ProcessThread.Abort();
                Synch.Completed = true;
            }
        }

        /// <summary>
        /// When the user double-clicks on the memory usage, run a full garbage collection
        /// </summary>
        /// <param name="sender"></param>
        /// <param name="e"></param>
        private void tsMemUse_DoubleClick(object sender, EventArgs e)
        {
            GC.Collect();
        }

        /// <summary>
        /// Handle the setting of the synch db flag
        /// </summary>
        /// <param name="sender"></param>
        /// <param name="e"></param>
        private void chkSynchDb_CheckedChanged(object sender, EventArgs e)
        {
            PerformSynch = chkSynchDb.Checked;
        }

        /// <summary>
        /// Show the NOMCR form
        /// </summary>
        /// <param name="sender"></param>
        /// <param name="e"></param>
        private void btnNOMCRs_Click(object sender, EventArgs e)
        {
            new frmNOMCRGenerator(MM_Configuration.xConfig["MacomberMap_Loader"]["NOMCR_Generation"].Attributes["RuleDatabase"].Value, CIMDb).Show();
        }

        /// <summary>
        /// Update our node to resource node handling
        /// </summary>
        /// <param name="sender"></param>
        /// <param name="e"></param>
        private void chkNodeToResourceNode_CheckedChanged(object sender, EventArgs e)
        {
            MacomberMap.Common.Database_Interface.CIM.Components.CIM_Element.ResourceNodesFromConnectivityNodes = chkNodeToResourceNode.Checked;
        }

        private void picLoadProgress_Click(object sender, EventArgs e)
        {

        }       
    }
}